package com.sghd.football.module.game.service.core;

import com.sghd.common.utils.math.MathUtils;
import com.sghd.football.module.data.service.DataService;
import com.sghd.football.module.game.facade.model.JoinResult;
import com.sghd.football.module.game.service.config.model.ConfigRoom;
import com.sghd.football.module.game.service.model.Medal;
import com.sghd.football.module.game.service.model.PropType;
import com.sghd.football.module.game.service.thread.Task;
import com.sghd.football.module.game.service.thread.Work;
import com.sghd.football.net.PushReceiver;
import com.sghd.football.utils.ManagedException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import static com.sghd.football.module.game.service.model.Window.*;


/**
 * \* Created with 王创辉.
 * \* Date: 2018/5/22
 * \* Time: 9:41
 * \* @desc:
 */
public class Room {
    private Logger logger = LoggerFactory.getLogger(Room.class);

    private int id;
    /*** 桌子列表<桌子id, 桌子>*/
    private ConcurrentMap<Integer, Desk> desks = new ConcurrentHashMap<>();
    /*** 玩家对应桌子<玩家id, 桌子>*/
    private ConcurrentMap<Long, Desk> player2Desk = new ConcurrentHashMap<>();
    private ConfigRoom configRoom;
    /*** 任务处理器*/
    private Work work;
    private DataService dataService;
    private PushReceiver receiver;
    /*** 反分池*/
    public ConcurrentMap<Integer, AtomicLong> refundPools = new ConcurrentHashMap<>();
    /*** 4个国家对应奖池*/
    public ConcurrentMap<Medal, AtomicLong> rewardPool = new ConcurrentHashMap<>();
    /** 系统抽水池*/
    public AtomicLong drawWaters = new AtomicLong();
    /*** 小游戏奖池锁*/
    public Lock rewardLock = new ReentrantLock(true);
    /*** 房间内所有空闲的桌子id集合*/
    private List<Integer> indexs = new ArrayList<>();

    public Room(Work timer, int roomId, ConfigRoom configRoom, DataService dataService, PushReceiver receiver, AtomicInteger counter) {
        this.work = timer;
        this.configRoom = configRoom;
        this.id = roomId;
        this.dataService = dataService;
        this.receiver = receiver;
        for (Medal country : Medal.values()) {//初始化反分池
            rewardPool.put(country, new AtomicLong());
        }
        for (int i = 0; i < configRoom.getPlayerLimit(); i++) {
            indexs.add(counter.getAndIncrement());
        }
        //初始化每个档的反分池
        for (int i = 0; i < configRoom.getBetArea().length; i++) {
            refundPools.put(i, new AtomicLong(0l));
        }
    }

    public JoinResult enter(Player player) {
        canJoin(player);
        if (player2Desk.containsKey(player.getPlayerId())) {
            if (player.isReconn()) {
                player.setReconn(false);
                //重连
                Desk desk = desks.get(player.getDeskId());
                return desk.reconn(player);
            } else {
                throw new ManagedException(PLAYER_ALREADY_INROOM);
            }
        }
        //正常加入游戏
        Desk desk = new Desk(indexs.remove(0), this, receiver, configRoom, dataService);
        desks.put(desk.getId(), desk);
        player2Desk.put(player.getPlayerId(), desk);
        desk.enter(player);
        Integer[] betArry = configRoom.getBetArea();
        JoinResult result = JoinResult.valueOf(betArry, player.getMondey(PropType.GOLD), player.getNickName(),
                player.getVipLevel(), player.getHeadUrl(), player.getFreeCount(), configRoom.getFreeCount(), rewardPool, player.getFreeMoney());
        return result;
    }

    /**
     * 更新奖池
     * @param totalBet 玩家下注金额
     */
    public void updatePool(long totalBet) {
        rewardLock.lock();
        try {
            //平均每个奖池的增量值
            long reward = (long) (((totalBet * configRoom.getRewardRate())) / Medal.values().length);//0.04
            for (AtomicLong pool : rewardPool.values()) {
                pool.set(MathUtils.overstep(pool.get(), reward));
            }
            //抽水
            long water = (long) (totalBet * configRoom.getDrawWaters());//系统抽水 0.02
            drawWaters.set(MathUtils.overstep(drawWaters.get(), water));
        } catch (Exception e) {
            throw e;
        } finally {
            rewardLock.unlock();
        }
    }

    public void canJoin(Player player) {
        //房间人数限制检查
        if (indexs.size() == 0) {//房间内没有空桌了
            throw new ManagedException(ROOM_LIMIT);
        }
        //金币入场限制检查
        Long[] limits = configRoom.getLimitMoney();
        long money = player.getMondey(PropType.GOLD);
        if (money < limits[0]) {
            throw new ManagedException(MONEY_TOOLOW);
        }
        if (money >= limits[1]) {
            throw new ManagedException(MONEY_TOOMANY);
        }
    }

    public Map<String, Object> operator(Player player, int betIdx) throws Exception {
        if (!player2Desk.containsKey(player.getPlayerId())) {
            throw new ManagedException(PLAYER_NOT_FOUND);
        }
        long playerId = player.getPlayerId();
        Desk desk = player2Desk.get(playerId);
        return desk.operator(playerId, betIdx);
    }

    public void exit(Player player, boolean rely) {
        Desk desk = player2Desk.get(player.getPlayerId());
        if (desk != null) {
            desk.exit(player, rely);
        }
        if (rely) {
            indexs.add(desk.getId());//桌子牌空闲中
            Collections.sort(indexs);
            desks.remove(desk.getId());
            player2Desk.remove(player.getPlayerId());
            desk = null;
        }
    }

    public boolean isEmpty() {
        if (desks.isEmpty() && player2Desk.isEmpty()) {
            return true;
        }
        return false;
    }

    public int getId() {
        return id;
    }

    public DataService getDataService() {
        return dataService;
    }

    public void submit(Desk desk, Task task) {
        work.submit(desk, task);
    }

    public void addTimer(Desk desk, Task task, Date time) {
        work.addTimer(desk, task, time);
    }

    public long getRefundPool(int index) {
        return refundPools.get(index).get();
    }

    public AtomicLong getRefund(int index) {
        return refundPools.get(index);
    }

    public ConcurrentMap<Medal, AtomicLong> getRewardPool() {
        return rewardPool;
    }

}
